package cn.tedu.v2_1;

import java.io.*;
import java.net.BindException;
import java.net.ServerSocket;
import java.net.Socket;

/**
 *  Version 2.1多人聊天室
 *  知识点：多线程
 *  实现步骤：1.Server类---创建多线程CS：目的：处理多个客户端发来的链接
 *           1.1找到Server类---主方法下：加入内部类，
 *              目的：供本类任意地方使用
 *              结构：private class CS implements Runnable{}
 *           1.2在内部类多线程CS---私有化属性：Socket--表示线程中socket对象
 *              在CS所线程类：private Socket socket;
 *           1.3创建构造器
 *           目的：通过线程创建--传入一个通过IP+端口号链接成功客户端socket
 *           public CS(Socket s){this.socket=s;}
 *           返回正确客户端socket
 *           理解：已经拨通服务器端的客户端
 *           语法块：this.socket=s;
 *           理解：当前线程socket=获取通过IP+端口，返回争取额客户端；
 *           1.4如果已经通过IP+端口得到socket中的正确读入
 *           理解：来一个客户端--服务器端分配：专门socket，进行读入操作
 *           对应：CS线程中 run() 实现读入功能
 *          2.Server类,start方法：第一件事：无限次接受客户端
 *                               第二件事：利用线程---客户端分配正确链接
 *                                        进行读入操作
 */
public class Server {
    private ServerSocket serverSocket;

    public Server() {
        try {
            serverSocket = new ServerSocket(6666);
        } catch (BindException e) {
            System.out.println("端口号重复申请，建议换个端口号");
        } catch (IOException e) {
            System.out.println("申请端口号失败~~~");
        }
    }
    //二.服务器端启动start：第一件事：无限接受服务器链接：返回客户端socket
    //                   第二件事：CS线程类：处理所有链接服务器端客户端
    //                            处理各个客户端的写入功能--【读取】
    public void start() {
        try {
            while (true) {
                //  第一件事： 无限次接收客户端链接，范围客户端socket
                System.out.println("----等待用户端链接---");
                Socket socket = serverSocket.accept();
                String ip = socket.getInetAddress().getHostAddress();
                System.out.println(ip + "客户端链接成功了");
                //  第二件事：接受一个客户端链接--交给线程处理----读入功能
                //  效果：链接上一个客户端---有一个服务器端，正确读入功能
                //  多线程并发解决方案：来客户端---服务器使用线程--创建客户端链接
                CS cs=new CS(socket);//1.通过链接,返回socket
                Thread t1=new Thread(cs);//2.交给线程--读入功能
                t1.start();//3.启动线程
            }
        } catch (IOException e) {
            System.out.println("服务器端通讯断开了");
        } finally {
        }
    }


    public static void main(String[] args) throws IOException {
        Server server = new Server();
        server.start();
    }

    //1.多线程CS类：处理多个客户端连接的线程类--读入存在线程中
    //使用内部类原因：多线程在Server中可以随时使用---读入方法
    //问题：一个客户端链接服务器，需要流操作：一个客户端与服务器端说话【卡住】
    //把服务器端写---放入线程中---目的：来一个客户端---配上服务器写
    private class CS implements Runnable {
        //3.定义私有属性：给当前多线程类使用
        private Socket socket;//返回一个客户端通讯：socket

        //4.多个客户端---交给CS构造器确定---那个客户端--进行读入操作‘
        public CS(Socket s){
            //5.通过线程创建--传入socket---确定就是当前线程socket
            this.socket=s;
        }
        //2.服务端功能--读取--加入线程中
        public void run() {
            BufferedReader br = null;
            try {
                InputStream is = socket.getInputStream();
                InputStreamReader isr = new InputStreamReader(is, "UTF8");
                br = new BufferedReader(isr);
                String str = null;
                while (true) {
                    str = br.readLine();
                    System.out.println("客户端说的话：" + str);
                }
                }catch(NullPointerException e){
                    System.out.println("IO流传入对象，有对象为null,立马核对，建议：查看try块中对象可能赋值为null");
                } catch(UnsupportedEncodingException e){
                    System.out.println("客户端或者服务器端：字符集不一致");
                } catch(IOException e){
                    System.out.println("通讯过程中，传输出现错误！");
                } finally{
                    if (br != null)
                        try {
                            br.close();
                        } catch (IOException e) {
                            System.out.println("结束通讯：关闭失败");
                        }
                    if (socket != null)
                        try {
                            socket.close();
                        } catch (IOException e) {
                            System.out.println("通讯关闭失败");
                        }
                }
            }
        }
    }


